package com.thornbird.vehiclerecycling.config;

import com.thornbird.vehiclerecycling.exception.ForbiddenException;
import com.thornbird.vehiclerecycling.exception.InternalServerException;
import com.thornbird.vehiclerecycling.exception.NotFoundException;
import com.thornbird.vehiclerecycling.exception.UnauthorizedException;
import com.thornbird.vehiclerecycling.model.response.Result;
import com.thornbird.vehiclerecycling.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.ArrayList;
import java.util.List;

@RestControllerAdvice
public class ApiExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApiExceptionHandler.class);

    @InitBinder
    public void initBinder(WebDataBinder binder) { }

    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result errorHandler(HttpServletRequest request, IllegalArgumentException exception) {
        LOGGER.error(String.format("%s NOT VALID", request.getRequestURI()), exception);
        Result result = ResultUtil.badRequestError();
        result.setMessage(exception.getMessage());
        return result;
    }

    @ExceptionHandler(value = BindException.class)
    public Result errorHandler(HttpServletRequest request, BindException exception) {
        LOGGER.error(String.format("%s NOT VALID", request.getRequestURI()), exception);
        List<ObjectError> errors = exception.getBindingResult().getAllErrors();
        List<String> errorMessages = new ArrayList<>();
        errors.stream().forEach(x -> errorMessages.add(x.getDefaultMessage()));
        Result result = ResultUtil.badRequestError();
        result.setMessage(String.join("; ", errorMessages));
        return result;
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    public Result errorHandler(HttpServletRequest request, ConstraintViolationException exception) {
        LOGGER.error(String.format("%s NOT VALID", request.getRequestURI()), exception);
        Result result = ResultUtil.badRequestError();
        result.setMessage(exception.getMessage());
        return result;
    }

    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public Result errorHandler(HttpServletRequest request, MethodArgumentTypeMismatchException exception) {
        LOGGER.error(String.format("%s NOT VALID", request.getRequestURI()), exception);
        Result result = ResultUtil.badRequestError();
        result.setMessage(exception.getMessage());
        return result;
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result errorHandler(HttpServletRequest request, MethodArgumentNotValidException exception) {
        LOGGER.error(String.format("%s NOT VALID", request.getRequestURI()), exception);
        Result result = ResultUtil.badRequestError();
        result.setMessage("Validation failed for argument: " + exception.getBindingResult().getObjectName());
        return result;
    }

    @ExceptionHandler(value = ValidationException.class)
    public Result errorHandler(HttpServletRequest request, ValidationException exception) {
        LOGGER.error(String.format("%s IVALIDATION ERROR", request.getRequestURI()), exception);
        Result result = ResultUtil.badRequestError();
        result.setMessage(exception.getMessage());
        return result;
    }

    @ExceptionHandler(value = UnauthorizedException.class)
    public Result errorHandler(HttpServletRequest request, UnauthorizedException exception) {
        LOGGER.error(String.format("%s UNAUTHORIZED", request.getRequestURI()), exception);
        return ResultUtil.unauthorizedError();
    }

    @ExceptionHandler(value = ForbiddenException.class)
    public Result errorHandler(HttpServletRequest request, ForbiddenException exception) {
        LOGGER.error(String.format("%s FORBIDDEN", request.getRequestURI()), exception);
        return ResultUtil.forbiddenError();
    }

    @ExceptionHandler(value = NotFoundException.class)
    public Result errorHandler(HttpServletRequest request, NotFoundException exception) {
        LOGGER.error(String.format("%s NOT FOUND", request.getRequestURI()), exception);
        return ResultUtil.notFoundError();
    }

    @ExceptionHandler(value = InternalServerException.class)
    public Result errorHandler(HttpServletRequest request, InternalServerException exception) {
        LOGGER.error(String.format("%s INTERNAL SERVER ERROR", request.getRequestURI()), exception);
        return ResultUtil.internalServerError();
    }

    @ExceptionHandler(value = Exception.class)
    public Result errorHandler(HttpServletRequest request, Exception exception) {
        LOGGER.error(String.format("%s INTERNAL SERVER ERROR", request.getRequestURI()), exception);
        Result result = ResultUtil.internalServerError();
        result.setMessage(exception.getMessage());
        return result;
    }

}
